package modbus

import (
	"MqttCloudProtocolServer/publiclib/datamodel/dborm"
	"MqttCloudProtocolServer/publiclib/datamodel/redismodel"
	"MqttCloudProtocolServer/publiclib/datastorage"
	"MqttCloudProtocolServer/publiclib/dbcontrol"
	"MqttCloudProtocolServer/publiclib/helplib"
	"errors"
	"fmt"
	"strconv"
	"strings"
	"time"

	"gitee.com/lossage/goProtocols/modbus"
	"github.com/wonderivan/logger"
)

const (
	diStandard = 0
	u8         = 1
	s8         = 2
	u16        = 3
	s16        = 4
	u32be      = 5
	s32be      = 6
	u32le      = 7
	s32le      = 8
	u64be      = 9
	s64be      = 10
	u64le      = 11
	s64le      = 12
	f32be      = 13
	f32le      = 14
	f64be      = 15
	f64le      = 16
	dib0       = 101
	dib1       = 102
	dib2       = 103
	dib3       = 104
	dib4       = 105
	dib5       = 106
	dib6       = 107
	dib7       = 108
	dib8       = 109
	dib9       = 110
	dib10      = 111
	dib11      = 112
	dib12      = 113
	dib13      = 114
	dib14      = 115
	dib15      = 116
)

type WriteListener interface {
	GetWriteRegCallback(wcmd, addrs string, regNo int,
		tag *dborm.Tagstable,
		writeCallback func(*TagForControl) bool) func(string, float64) bool
}
type TagInfo struct {
	Tag     *dborm.Tagstable
	Cmd     *dborm.Modbuscommands
	RegNo   int
	Command int
	RegLen  int
}

type TagForControl struct {
	Tag     string
	Addr    uint8
	Value   []uint16
	CmdFunc modbus.CommandType
	RegNo   uint16
}

type ModbusDataAnalysis struct {
	tagIndex map[int][]*TagInfo
}

func (_this *ModbusDataAnalysis) Init(cmds []*dborm.Modbuscommands, writeCallback func(*TagForControl) bool) bool {
	_this.tagIndex = make(map[int][]*TagInfo)
	datastorage.GetRedisStorageContent()
	tagsTableControl := dbcontrol.CreateTagesTableControl()
	if tagsTableControl == nil {
		logger.Alert("获取Tags表控制类出错")
		return false
	}

	//读取寄存器归类整理
	for _, cmd := range cmds {
		tags, err := tagsTableControl.GetByIODeviceAndLogicEquipment(cmd.UUID, strconv.Itoa(cmd.Addr))
		if err != nil {
			logger.Alert("读取Tags出错:" + err.Error())
			continue
		}
		tagInfos := make([]*TagInfo, len(tags))
		i := 0
		for _, tag := range tags {
			if tagInfoTemp, err := AnalysisReadReg(tag.IODevice, tag.Reg, tag.DataType); err == nil {
				if tagInfoTemp == nil {
					continue
				}
				if tagInfoTemp.Command == cmd.Command && tagInfoTemp.RegNo >= cmd.RegStart && (tagInfoTemp.RegNo)+tagInfoTemp.RegLen <= (cmd.RegStart+cmd.RegCount) {
					tagInfoTemp.Cmd = cmd
					tagInfoTemp.Tag = tag
					tagInfos[i] = tagInfoTemp
					i++
				}
			} else {
				logger.Alert(err.Error())
			}
		}
		_this.tagIndex[cmd.ID] = tagInfos[:i]
	}

	//写入寄存器注册回调
	if len(cmds) > 0 {
		if err := AnalysisWriteReg(cmds[0].UUID, _this, writeCallback); err != nil {
			logger.Alert(err.Error())
		}
	}
	return true
}
func (_this *ModbusDataAnalysis) AnalysisData(cmd *dborm.Modbuscommands, receiveFrame *modbus.ModbusProtocol) {
	if regs, ok := _this.tagIndex[cmd.ID]; ok {
		for _, reg := range regs {
			res := DataDeal(receiveFrame.Body, reg)
			if res != nil {
				datastorage.GetRedisStorageContent().SaveToRedis("realData", res)
			}
		}
	} else {
		return
	}
}

func DataDeal(buffer []uint16, reg *TagInfo) *redismodel.RealData {
	data := float64(0)
	dataDis := ""
	if len(buffer) < (reg.RegNo + reg.RegLen - reg.Cmd.RegStart) {
		logger.Alert("数据长度不够")
		return nil
	}
	regIndexTemp := reg.RegNo - reg.Cmd.RegStart
	isNumber := true
	isDigital := true
	datares := float64(0)
	data, isDigital = DigitalCheckDeal(reg.Tag.DataType, buffer[regIndexTemp])
	if isDigital {
		if data != 0 {
			data = 1
		}
		if reg.Tag.Rate == -1 {
			data = 1 - data
		}
		string_slice := strings.Split(reg.Tag.Unit, ":")
		if len(string_slice) > 1 {
			if data == 0 {
				dataDis = string_slice[0]
			} else {
				dataDis = string_slice[1]
			}
		}
		datares = data
	}
	data, isNumber = NumberCheckDeal(reg.Tag.DataType, buffer[regIndexTemp:])
	// logger.Info("reg:" + reg.Tag.TagName + " data:" + fmt.Sprintf("%f", data))
	if isNumber {
		data = data * reg.Tag.Rate
		dataDis = strings.TrimRight(strings.TrimRight(fmt.Sprintf("%f", data), "0"), ".") + reg.Tag.Unit
		datares = data
	}
	return &redismodel.RealData{
		Key:      reg.Tag.TagName,
		Value:    datares,
		ValueDis: dataDis,
		LastTime: time.Now(),
	}
}
func UpdateComFlag(device string, addr int, comFlag int8, comDis string) {
	datastorage.GetRedisStorageContent().SaveToRedis("realData", &redismodel.RealData{
		Key:      device + "-comFlag",
		Value:    float64(comFlag),
		ValueDis: comDis,
		LastTime: time.Now(),
	})
}

// 对旧的寄存器格式做一下兼容处理
func AnalysisReadRegOldDeal(reg string) string {
	if !strings.Contains(reg, ":") {
		if res, err := strconv.Atoi(reg); err == nil {
			regNo := (res % 10000)
			if res < 10000 {
				return "LDI:" + strconv.Itoa(regNo)
			} else if res < 20000 {
				return "HDI:" + strconv.Itoa(regNo)
			} else if res < 30000 {
				return ""
			} else if res < 40000 {
				return "HAI:" + strconv.Itoa(regNo)
			} else if res < 50000 {
				return "LAI:" + strconv.Itoa(regNo)
			}
		}
	}
	return reg
}

// 解析寄存器地址
func AnalysisReadReg(iodev string, regparam string, dataType int) (*TagInfo, error) {
	reg := AnalysisReadRegOldDeal(regparam)
	if reg == "" {
		return nil, errors.New(iodev + "寄存器地址格式错误")
	}
	cmds := strings.Split(reg, ":")
	if len(cmds) < 2 {
		return nil, errors.New(iodev + "寄存器地址格式错误")
	}
	if res, err := strconv.Atoi(cmds[1]); err == nil {
		var cmdFunCode modbus.CommandType
		wcmd := strings.TrimSpace(strings.ToUpper(cmds[0]))
		switch wcmd {
		case "LDI":
			cmdFunCode = modbus.ReadCoilStatus
		case "HDI":
			cmdFunCode = modbus.ReadInputStatus
		case "LAI":
			cmdFunCode = modbus.ReadHoldingRegisters
		case "HAI":
			cmdFunCode = modbus.ReadInputRegisters
		//遥控格式不在这里处理，但也不报错
		case "SDO", "SAO", "MDO", "MAO":
			return nil, nil
		default:
			return nil, errors.New(iodev + ":" + wcmd + ":寄存器地址格式错误")
		}

		tag := new(TagInfo)
		tag.RegNo = res - 1
		tag.Command = int(cmdFunCode)
		switch dataType {
		case u32be, s32be, u32le, s32le, f32be, f32le:
			tag.RegLen = 2
		case u64be, s64be, u64le, s64le, f64be, f64le:
			tag.RegLen = 4
		default:
			tag.RegLen = 1
		}
		return tag, nil
	}
	return nil, errors.New(iodev + ":解析寄存器地址出错")
}

func AnalysisWriteReg(devid string, content WriteListener, writeCallback func(*TagForControl) bool) error {
	tagsTableControl := dbcontrol.CreateTagesTableControl()
	if tagsTableControl == nil {
		return errors.New(devid + "获取Tags表控制类出错")
	}
	tags, err := tagsTableControl.GetByIODevice(devid)
	if err != nil {
		return errors.New(devid + "读取Tags出错:" + err.Error())
	}
	for _, tag := range tags {
		if strings.Contains(tag.Reg, ":") {
			cmds := strings.Split(tag.Reg, ":")
			if len(cmds) > 1 {
				wcmd := strings.TrimSpace(strings.ToUpper(cmds[0]))
				switch wcmd {
				case "SDO", "SAO", "MDO", "MAO":
					if res, err := strconv.Atoi(cmds[1]); err == nil {
						datastorage.GetRedisStorageContent().RegistCallback(tag.TagName, content.GetWriteRegCallback(wcmd, tag.LogicEquipment, res, tag, writeCallback))
						//datastorage.GetRedisStorageContent().RegistCallback(tag.TagName, _this.analysisWriteRegCallback)
					} else {
						logger.Alert(devid + "遥控地址不为数字：" + tag.TagName)
						continue
					}
				case "LDI", "HDI", "LAI", "HAI":
					continue
				default:
					logger.Alert(devid + "命令不支持：" + tag.TagName)
					continue
				}
			}
		}
	}
	return nil
}
func (_this *ModbusDataAnalysis) GetWriteRegCallback(wcmd, paramAddrs string, paramRegNo int,
	paramTag *dborm.Tagstable, writeCallback func(*TagForControl) bool) func(string, float64) bool {

	tag := paramTag
	addrs := paramAddrs
	regNo := paramRegNo

	backFunc := func(tagName string, value float64) bool {
		var cmdFunCode modbus.CommandType
		var valueBuffer = []uint16{}
		rate := tag.Rate
		if rate == 0 {
			rate = 1
		}
		switch wcmd {
		case "SDO":
			cmdFunCode = modbus.ForceSingleCoil
			valueBuffer = make([]uint16, 1)
			if rate == 1 {
				if value == 0 {
					valueBuffer[0] = 0x0000
				} else {
					valueBuffer[0] = 0xff00
				}
			} else if rate == -1 {
				if value == -1 {
					valueBuffer[0] = 0xff00
				} else {
					valueBuffer[0] = 0x0000
				}
			}
		case "SAO":
			cmdFunCode = modbus.PresetSingleRegister
			valueBuffer = make([]uint16, 1)
			value = value / rate
			if tag.DataType == s16 {
				if temp, err := helplib.Float64ToUShortByS16(value); err != nil {
					logger.Alert(err.Error() + tag.TagName)
					return false
				} else {
					valueBuffer[0] = temp
				}
			} else if tag.DataType == u16 {
				if temp, err := helplib.Float64ToUShortByU16(value); err != nil {
					logger.Alert(err.Error() + tag.TagName)
					return false
				} else {
					valueBuffer[0] = temp
				}
			} else {
				logger.Alert("数据类型在单点遥控中不受支持" + tag.TagName)
				return false
			}
		case "MDO":
			cmdFunCode = modbus.ForceMultipleCoils
			valueBuffer = make([]uint16, 1)
			if rate == 1 {
				if value == 0 {
					valueBuffer[0] = 0
				} else {
					valueBuffer[0] = 1
				}
			} else if rate == -1 {
				if value == -1 {
					valueBuffer[0] = 1
				} else {
					valueBuffer[0] = 0
				}
			}
		case "MAO":
			cmdFunCode = modbus.PresetMultipleRegisters
			value = value / rate
			var err error = nil
			switch tag.DataType {
			case s16:
				valueBuffer = make([]uint16, 1)
				if temp, err := helplib.Float64ToUShortByS16(value); err != nil {
					logger.Alert(err.Error() + tag.TagName)
					return false
				} else {
					valueBuffer[0] = temp
				}
			case u16:
				valueBuffer = make([]uint16, 1)
				if temp, err := helplib.Float64ToUShortByU16(value); err != nil {
					logger.Alert(err.Error() + tag.TagName)
					return false
				} else {
					valueBuffer[0] = temp
				}
			case s32be:
				if valueBuffer, err = helplib.Float64ToUShortsBeByS32(value); err != nil {
					logger.Alert(err)
					return false
				}
			case u32be:
				if valueBuffer, err = helplib.Float64ToUShortsBeByU32(value); err != nil {
					logger.Alert(err)
					return false
				}
			case s32le:
				if valueBuffer, err = helplib.Float64ToUShortsLeByS32(value); err != nil {
					logger.Alert(err)
					return false
				}
			case u32le:
				if valueBuffer, err = helplib.Float64ToUShortsLeByU32(value); err != nil {
					logger.Alert(err)
					return false
				}
			case s64be:
				if valueBuffer, err = helplib.Float64ToUShortsBeByS64(value); err != nil {
					logger.Alert(err)
					return false
				}
			case u64be:
				if valueBuffer, err = helplib.Float64ToUShortsBeByU64(value); err != nil {
					logger.Alert(err)
					return false
				}
			case s64le:
				if valueBuffer, err = helplib.Float64ToUShortsLeByS64(value); err != nil {
					logger.Alert(err)
					return false
				}
			case u64le:
				if valueBuffer, err = helplib.Float64ToUShortsLeByU64(value); err != nil {
					logger.Alert(err)
					return false
				}
			case f32be:
				if valueBuffer, err = helplib.Float64ToUShortsBeByF32(value); err != nil {
					logger.Alert(err)
					return false
				}
			case f32le:
				if valueBuffer, err = helplib.Float64ToUShortsLeByF32(value); err != nil {
					logger.Alert(err)
					return false
				}
			case f64be:
				if valueBuffer, err = helplib.Float64ToUShortsBe(value); err != nil {
					logger.Alert(err)
					return false
				}
			case f64le:
				if valueBuffer, err = helplib.Float64ToUShortsLe(value); err != nil {
					logger.Alert(err)
					return false
				}
			default:
				logger.Alert("Unknown type")
				return false
			}
		default:
			logger.Alert("不支持的控制命令：" + tag.TagName)
			return false
		}
		if addr, err := strconv.Atoi(addrs); err == nil {
			if addr > 255 || addr < 1 {
				logger.Alert("遥控地址超出范围许可：" + tag.TagName)
				return false
			}
			return writeCallback(&TagForControl{
				Tag:     tag.TagName,
				Addr:    uint8(addr),
				Value:   valueBuffer,
				CmdFunc: cmdFunCode,
				RegNo:   uint16(regNo - 1),
				//TODO: Not sure if this is correct
				//RegNo:   (uint16)((regNo % 10000) - 1),
			})
		} else {
			logger.Alert("遥控地址转换错误：" + err.Error())
			return false
		}
	}
	return backFunc
	//return nil
}
func DigitalCheckDeal(datatype int, regData uint16) (float64, bool) {
	data := float64(0)
	isDigital := true
	switch datatype {
	case diStandard:
		data = float64(regData)
	case dib0:
		data = float64(regData & 0x0001)
	case dib1:
		data = float64(regData >> 1 & 0x0001)
	case dib2:
		data = float64(regData >> 2 & 0x0001)
	case dib3:
		data = float64(regData >> 3 & 0x0001)
	case dib4:
		data = float64(regData >> 4 & 0x0001)
	case dib5:
		data = float64(regData >> 5 & 0x0001)
	case dib6:
		data = float64(regData >> 6 & 0x0001)
	case dib7:
		data = float64(regData >> 7 & 0x0001)
	case dib8:
		data = float64(regData >> 8 & 0x0001)
	case dib9:
		data = float64(regData >> 9 & 0x0001)
	case dib10:
		data = float64(regData >> 10 & 0x0001)
	case dib11:
		data = float64(regData >> 11 & 0x0001)
	case dib12:
		data = float64(regData >> 12 & 0x0001)
	case dib13:
		data = float64(regData >> 13 & 0x0001)
	case dib14:
		data = float64(regData >> 14 & 0x0001)
	case dib15:
		data = float64(regData >> 15 & 0x0001)
	default:
		isDigital = false
	}
	return data, isDigital
}
func NumberCheckDeal(datatype int, buffer []uint16) (float64, bool) {
	data := float64(0)
	isNumber := true
	switch datatype {
	case u8:
		data = float64(uint8(buffer[0] & 0x00ff))
	case s8:
		data = float64(int8(buffer[0] & 0x00ff))
	case u16:
		data = float64(uint16(buffer[0]))
	case s16:
		data = float64(helplib.UShortsToInt16(buffer[0]))
	case u32be:
		if res, err := helplib.UShortsToUInt32Be(buffer[0:2]); err == nil {
			data = float64(res)
		} else {
			logger.Info("数据解析错误")
			return 0, false
		}
	case s32be:
		if res, err := helplib.UShortsToInt32Be(buffer[0:2]); err == nil {
			data = float64(res)
		} else {
			logger.Info("数据解析错误")
			return 0, false
		}
	case u32le:
		if res, err := helplib.UShortsToUInt32Le(buffer[0:2]); err == nil {
			data = float64(res)
		} else {
			logger.Info("数据解析错误")
			return 0, false
		}
	case s32le:
		if res, err := helplib.UShortsToInt32Le(buffer[0:2]); err == nil {
			data = float64(res)
		} else {
			logger.Info("数据解析错误")
			return 0, false
		}
	case u64be:
		if res, err := helplib.UShortsToUInt64Be(buffer[0:4]); err == nil {
			data = float64(res)
		} else {
			logger.Info("数据解析错误")
			return 0, false
		}
	case s64be:
		if res, err := helplib.UShortsToInt64Be(buffer[0:4]); err == nil {
			data = float64(res)
		} else {
			logger.Info("数据解析错误")
			return 0, false
		}
	case u64le:
		if res, err := helplib.UShortsToUInt64Le(buffer[0:4]); err == nil {
			data = float64(res)
		} else {
			logger.Info("数据解析错误")
			return 0, false
		}
	case s64le:
		if res, err := helplib.UShortsToInt64Le(buffer[0:4]); err == nil {
			data = float64(res)
		} else {
			logger.Info("数据解析错误")
			return 0, false
		}
	case f32be:
		if res, err := helplib.UShortsToReal32Be(buffer[0:2]); err == nil {
			data = float64(res)
		} else {
			logger.Info("数据解析错误")
			return 0, false
		}
	case f32le:
		if res, err := helplib.UShortsToReal32Le(buffer[0:2]); err == nil {
			data = float64(res)
		} else {
			logger.Info("数据解析错误")
			return 0, false
		}
	case f64be:
		if res, err := helplib.UShortsToReal64Be(buffer[0:4]); err == nil {
			data = float64(res)
		} else {
			logger.Info("数据解析错误")
			return 0, false
		}
	case f64le:
		if res, err := helplib.UShortsToReal64Le(buffer[0:4]); err == nil {
			data = float64(res)
		} else {
			logger.Info("数据解析错误")
			return 0, false
		}
	default:
		isNumber = false
	}
	return data, isNumber
}
